#ifndef SROS_DEVICE_DETAIL_PROTO_HPP
#define SROS_DEVICE_DETAIL_PROTO_HPP

#include <commons/impl/noncopyable.hpp>
#include <commons/type/ret.hpp>

#include <commons/hal/io.hpp>
#include <commons/hal/can.hpp>
#include <commons/hal/canopen.hpp>
#include <commons/hal/uart.hpp>

#include <functional>
#include <string>
#include <map>

namespace sros::device
{
    class proto : public impl::noncopyable
    {
    public:
        static proto &ins();
        ~proto() = default;

        // 指令码
        typedef enum : uint8_t
        {
            Unkown = 0x00,   // 未知指令
            Version = 0x01,  // 获取版本指令
            Init = 0x02,     // 初始化指令
            Reset = 0x03,    // 重启指令
            Trans = 0x04,    // 透传指令
            Status = 0x05,   // 外接接口状态
            Heartbeat = 0x06,// 心跳包
            Power  = 0x07,   // 电源引脚状态
            Encoder = 0x08   // 外置编码器
        } Cmd;

        // 控制板初始化，初始化接口类型
        typedef enum : uint8_t
        {
            CAN = 1U,       // CAN口
            UART = 2U,      // 串口
            INPUTIO = 3U,   // 输入IO口
            OUTPUTIO = 4U   // 输出IO口
        } ConfigType;

        // 电源控制设备类型
        typedef enum : uint8_t
        {
            Power_Min = 0,
            Motor   = 1,    // 电机
            Ptz     = 2,    // 云台
            Arm     = 3,    // arm
            Cam1    = 4,    // 左相机
            Cam2    = 5,    // 右相机
            Env     = 6,    // 环感
            Rfid    = 7,    // rfid感应器
            FishEye = 8,    // 鱼眼
            Can     = 9,    // can
            Power_Max = 10
        } PowerType;

        
        typedef enum : uint8_t
        {
            Encoder_Min = 0,
            Encoder_H   = 1,
            Encoder_V   = 2,
            Encoder_Max = 3
        } EncoderType;

        #pragma pack(1)

        // 控制板初始化，CAN口初始化数据结构
        typedef struct 
        {
            ConfigType  type;      // 类型
            uint16_t    index;     // 索引
            uint16_t    prescaler; // 预分频参数 [1, 1024]
            uint8_t     seg1;      // 时间分段1
            uint8_t     seg2;      // 时间分段2
        } CanConfig;

        // 控制板初始化，串口初始化数据结构
        typedef struct
        {
            ConfigType  type;           // 类型
            uint16_t    index;          // 索引
            uint32_t    bitrate;        // 比特率
            uint8_t     wordlength;     // 数据位
            uint8_t     stopbit;        // 停止位
            hal::ParityType  parity;    // 校验位
        } UartConfig;

        // 控制板初始化，输入IO初始化数据结构
        typedef struct
        {
            ConfigType type;              // 类型
            hal::IOPort     port;         // 端口 [A,B,C,D,E,F,G,H,I]
            uint8_t    pin;               // PIN号
            hal::IOMode     mode;         // IO模式
            hal::IOPullMode pull_up_down; // 上拉下拉
        } InputIOConfig;

        // 控制板初始化，输出IO初始化数据结构
        typedef struct 
        {
            ConfigType type;             // 类型
            hal::IOPort port;            // 端口 [A,B,C,D,E,F,G,H,I]
            uint8_t pin;                 // PIN号
            hal::IOMode mode;            // IO模式
            hal::IOPullMode pull_up_down;// 上拉下拉
            hal::IOOutLevel out_level;   // 输出类型
            hal::IOSpeed spd;            // 输出速度
        } OutputIOConfig;

        //////////////////// Request/Response ////////////////////

        // CAN外设接口状态
        typedef struct
        {
            uint8_t  active;
            uint16_t index;
            uint8_t state;
            uint32_t error;
        } RspCanState;

        // UART外设接口状态
        typedef struct
        {
            uint8_t         active;
            uint16_t        index;
            uint8_t         state;
            uint32_t        error;
        } RspUartState;

        // IO口反馈数据
        typedef struct 
        {
            hal::IOPort     port; // 端口
            uint8_t         pin;  // PIN号
            hal::IOOutLevel lvl;  // 输出
        } RspIoMsg;

        // 串口反馈数据
        typedef struct
        {
            uint16_t index;              // 索引
            std::vector<uint8_t> buffer; // 串口实际数据
        } RspUartMsg;

        // Can口反馈数据
        typedef struct
        {
            uint16_t index;     // 索引
            hal::CanRxMsg msg;  // can口数据
        } RspCanMsg;

        // 初始化指令反馈
        typedef struct
        {
            ConfigType  type;    // 类型
            uint16_t    index;   // 索引
            uint8_t     ec;      // 错误码 0: no error  1: error
            std::string msg;     // 错误信息
        } RspInitMsg;

        // 版本信息反馈数据
        typedef struct
        {
            std::string sn;       // sn
            std::string version;  // 版本
            std::string datetime; // 日期
        } RspVersionMsg;

        // 往Can口发送数据
        typedef struct 
        {
            uint16_t index;      // 索引
            hal::CanTxMsg can_msg; // can口数据
        } ReqCanMsg;

        // 往串口发送数据
        typedef struct
        {
            uint16_t index;     // 索引
            std::vector<uint8_t> data; // 串口数据
        } ReqUartMsg;

        // 往IO发送数据
        typedef struct 
        {
            hal::IOPort port;
            uint8_t pin;
            hal::IOOutLevel lvl;
        } ReqIoMsg;

        typedef struct 
        {
            uint8_t active = 0;
            uint16_t index = 0;
            uint32_t error = 0;
            uint8_t  state = 0;
            std::string msg;
        } CanStatus;

        typedef struct
        {
            uint8_t active = 0;
            uint16_t index = 0;
            uint32_t error = 0;
            uint8_t state  = 0;
            std::string msg;
        } UartStatus;

        #pragma pack()

        /**
         * @brief    打包获取版本指令协议数据
         * @note
         * @see
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_version();

        /**
         * @brief    打包初始化指令协议数据
         * @note     Can口初始化参数
         * @see
         * @param    CanConfig: Can口初始化参数
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_init_board(const CanConfig &t);

        /**
         * @brief    打包初始化指令协议数据
         * @note     串口初始化参数
         * @see
         * @param    UartConfig: 串口初始化参数
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_init_board(const UartConfig &t);

        /**
         * @brief    打包初始化指令协议数据
         * @note     输入IO初始化参数
         * @see
         * @param    InputIOConfig: 输入IO初始化参数
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_init_board(const InputIOConfig &t);

        /**
         * @brief    打包初始化指令协议数据
         * @note     输出IO初始化参数
         * @see
         * @param    OutputIOConfig: 输出IO初始化参数
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_init_board(const OutputIOConfig &t);

        /**
         * @brief    打包心跳指令协议数据
         * @note
         * @see
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_heartbeat();

        /**
         * @brief    打包重置指令协议数据
         * @note
         * @see
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_reset();

        /**
         * @brief    根据给定的参数，按照协议打包CAN数据
         * @note
         * @see
         * @param    index: 数据通道号
         *           CanTxMsg: 要发送的CAN报文数据
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_trans(const ReqCanMsg &msg);

        /**
         * @brief    根据给定的参数，按照协议打包串口数据
         * @note
         * @see
         * @param    index: 数据通道号
         *           payload: 要发送的串口数据
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_trans(const ReqUartMsg &msg);

        /**
         * @brief    根据给定的参数，按照协议打包串口数据
         * @note
         * @see
         * @param    index: 数据通道号
         *           payload: 要发送的串口数据
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_trans(const ReqIoMsg &msg);

        /**
         * @brief    根据给定的参数，按照协议打包电源控制数据
         * @note
         * @see
         * @param    id: 设备ID
         *           is_on: 要发送的电源控制数据
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_power(const uint8_t &id, const uint8_t &is_on);

        /**
         * @brief    根据给定的参数，按照协议打包编码器赋值数据
         * @note
         * @see
         * @param    id: 编码器ID
         *           pulse: 要发送的编码器数据
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_encoder(const uint8_t &id, const int32_t &pulse);

        /**
         * @brief    根据给定的指令码，打包一个空的协议包
         * @note
         * @see
         * @param    cmd: 指令码
         * @return   返回按照通讯协议打包后的数据
         *
         * @warning
         */
        std::vector<uint8_t> pck_empty(const Cmd &cmd);

        /**
         * @brief    数据解码的统一入口，解码成功，会自动调用对应指令类型的回调函数
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack(const std::vector<uint8_t> &payload);

        // 初始化指令反馈回调函数
        using InitCb = std::function<void(const RspInitMsg &)>;
        inline void install_init_cb(InitCb cb) { init_cb_ = cb; }

        // 版本信息反馈回调函数
        using VersionCb = std::function<void(const RspVersionMsg &)>;
        inline void install_version_cb(VersionCb cb) { version_cb_ = cb; }
        
        // CAN数据到达回调函数
        using CanCb = std::function<void(const RspCanMsg &)>;
        inline void install_can_cb(CanCb cb) { can_cb_ = cb; }

        // 串口数据到达回调函数
        using UartCb = std::function<void(const RspUartMsg &)>;
        inline void install_uart_cb(UartCb cb) { uart_cb_ = cb; }

        // IO数据到达回调函数
        using IoCb = std::function<void(const RspIoMsg &)>;
        inline void install_io_cb(IoCb cb) { io_cb_ = cb; }

        // CAN外设接口状态回调函数
        using CanStateCb = std::function<void(const RspCanState &)>;
        inline void install_can_state(CanStateCb cb) { can_state_cb_ = cb; }

        // UART外设接口状态回调函数
        using UartStateCb = std::function<void(const RspUartState &)>;
        inline void install_uart_state(UartStateCb cb) { uart_state_cb_ = cb; }

        // Power外设接口状态回调函数
        using PowerStateCb = std::function<void(const std::map<uint32_t, bool> &)>;
        inline void install_power_state(PowerStateCb cb) { power_state_cb_ = cb; }

        // 编码器数据达到回调函数
        using EncoderCb = std::function<void(const std::map<uint32_t, int32_t> &)>;
        inline void install_encoder_cb(EncoderCb cb) { encoder_cb_ = cb; }

    private:
        proto() = default;

        /**
         * @brief    解析数据包，获取指令码和对应的负载数据
         * @note
         * @see
         * @param    cmd: 指令码
         *           buffer: 负载数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_msg(const std::vector<uint8_t> &payload, uint8_t &cmd, std::vector<uint8_t> &buffer);

        /**
         * @brief    通过指令码分发对应的负载数据，进行进一步解码
         * @note
         * @see
         * @param    cmd: 指令码
         *           buffer: 负载数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_dispatch(const uint8_t &cmd, const std::vector<uint8_t> &payload);

        /**
         * @brief    进一步解码初始化指令的反馈数据
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         *           out: 解析后的数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_init_msg(const std::vector<uint8_t> &payload, RspInitMsg &out);

        /**
         * @brief    进一步解码版本信息的反馈数据
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         *           out: 解析后的数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_version_msg(const std::vector<uint8_t> &payload, RspVersionMsg &out);

        /**
         * @brief    进一步解码CAN报文的反馈数据CAN报文的反馈数据
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         *           out: 解析后的数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_can_msg(const std::vector<uint8_t> &payload, RspCanMsg &out);

        /**
         * @brief    进一步解码串口的反馈数据
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         *           out: 解析后的数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_uart_msg(const std::vector<uint8_t> &payload, RspUartMsg &out);

        /**
         * @brief    进一步解码IO的反馈数据
         * @note     
         * @see
         * @param    payload: 需要解码的负载数据
         *           out: 解析后的数据
         * @return   RetBool
         *
         * @warning
         */
        RetBool unpack_io_msg(const std::vector<uint8_t> &payload, RspIoMsg &out);

        RetBool unpack_can_state(const std::vector<uint8_t> &payload, RspCanState &out);

        RetBool unpack_uart_state(const std::vector<uint8_t> &payload, RspUartState &out);

        RetBool unpack_power_state(const std::vector<uint8_t> &payload, std::map<uint32_t, bool> &out);

        RetBool unpck_encoder_msg(const std::vector<uint8_t> &payload, std::map<uint32_t, int32_t> &out);

    private:
        InitCb       init_cb_;         // 初始化配置指令，反馈回调接口
        VersionCb    version_cb_;      // 控制板信息指令，反馈回调接口
        CanCb        can_cb_;          // Can口数据，反馈回调接口
        UartCb       uart_cb_;         // 串口数据, 反馈回调接口
        IoCb         io_cb_;           // IO口, 反馈回调接口
        CanStateCb   can_state_cb_;    // CAN外设接口状态回调函数
        UartStateCb  uart_state_cb_;   // 串口外设接口状态回调函数
        PowerStateCb power_state_cb_;  // Power外设接口状态回调函数
        EncoderCb    encoder_cb_;      // 编码器数据达到回调函数
    };
}

#endif